import Cocoa

//var str = "Hello, playground"


/**
 类的初始化和反初始化
 1. 初始化器: 初始化器在创建特定类型的实例时被调用
 
 */

//1. 初始化器: 初始化器在创建特定类型的实例时被调用
struct Fahrenheit {
    var temprature: Double
    init() {
        temprature = 3.2   //在初始化器里为存储属性设置初始值
    }
}
var f = Fahrenheit()
print("默认温度是\(f.temprature)℃")


//默认的属性值: 你可以在初始化器里为存储属性设置初始值. 另外, 指定一个默认属性值作为属性声明的一部分. 当属性被定义的时候你可以通过为这个属性分配一个初始值来指定默认的属性值
struct Fahrenheit2 {
    var temperature = 3.2   //当属性被定义的时候设置属性的初始值作为属性的默认值, 可以不需要在初始化器里设置初始值
}
var f2 = Fahrenheit2()
print("默认温度是\(f2.temperature)度")


//默认的初始化器: Swift为所有没有初始化器的结构体或类提供了一个默认的初始化器来给所有的属性提供了默认值.
class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()
print("name is \(item.name), quantity is \(item.quantity)")

//自定义初始化器: 可以提供初始化形式参数作为初始化器的一部分, 类定义初始化过程中的类型和值的名称. 初始化形式参数与函数和方法的形式参数具有相同的功能和语法
struct Celsius {
    var temperature: Double
    init(fromFahrenheit fahrenheit: Double) {   //相当于函数中的实际参数标签和形式参数名
        temperature = (fahrenheit - 32.0) / 2.0
    }
    init(fromKelvin kelvin: Double) {
        temperature = kelvin - 200.0
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromKelvin: 200.0)
print("boil is \(boilingPointOfWater.temperature), freez is \(freezingPointOfWater)")


//在初始化中分配常量属性: 在初始化的任意时刻,你都可以给常量属性赋值, 只要他在初始化结束时设置了确定的值即可. 一旦常量属性被赋值,他就不能再被修改了
class SurveyQuestion {
    let text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
beetsQuestion.response = "I also like beets"
//beetsQuestion.text = "啦啦啦啦,我来啦!!!"  //这里会报错,因为text是常量属性,前面这个属性已经被赋值了,在这里就不能赋值了


//结构体的成员初始化器: 没有结构体中没有定义任何自定义初始化器,它会自动获得一个成员初始化器. 不同于默认初始化器,结构体会接收成员初始化器,即使他的存储属性没有默认值
//struct Size {
//    var width = 0.0
//    var height = 0.0
//}
//let twoByTwo = Size(width: 2.0, height: 2.0)


//值类型的初始化器的委托: 初始化器可以调用其他初始化器来执行部分实例的初始化. 这个过程,就是所谓的初始化器的委托.避免了多个初始化器的冗余代码
struct Size {
    var width = 0.0
    var height = 0.0
}
struct Point {
    var x = 0.0
    var y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    init() {}  //第一个初始化器.没有任何操作
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width) / 2
        let originY = center.y - (size.height) / 2
        self.init(origin: Point(x: originX, y: originY), size: size)  //调用了第二个初始化器
    }
}
var origin = Point(x: 9.0, y: 5.0)
var size = Size(width: 20.0, height: 20.0)
print("中心点是:\(size.width / 2),\(size.height / 2)")

/**
 类的继承和初始化
 1. 所有类的存储属性(包括从他的父类继承的所有属性)都必须在初始化期间分配初始值
 2. Swift为类类型定义了两种初始化器以确保所有的存储属性接收一个初始值. 这些就是所谓的指定初始化器和便捷初始化器.
 3. 指定初始化器是类的主要初始化器. 指定的初始化器可以初始化所有那个类引用的属性并且调用调用合适的父类初始化器来继续这个初始化过程给父类链
 4. 类偏向于少量指定初始化器,并且类通常只有一个指定初始化器.
 5. 每一个类至少得有一个指定初始化器.
 6. 便捷初始化器是次要的
 7. 如果你的类不需要便捷初始化器可以不提供它
 */

//指定初始化器
//init (parameters) {
//    statements
//}

//便捷初始化器(就是在init关键字前面加上convenience修饰符)
//convenience init (parameters) {
//    statements
//}

/**
 安全检查:
 1. 指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成
 2. 指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值. 如果不这样做,指定初始化器赋予的新值会被父类的初始化器所覆盖
 3. 便捷初始化器必须先委托同类中的其他初始化器,然后再为任意属性赋新值(包括同类里定义的属性). 如果没这么做,便捷初始化器赋予的新值将被自己类中其他指定初始化器所覆盖
 4. 初始化器在第一阶段初始化完成之前(初始化父类之前),不能调用任何实例方法,不能读取任何实例属性的值,也不能引用self作为值
*/

class Person {
    var name: String
    var age: Int
    //必要初始化器: 在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须实现该初始化器。
    required init(name: String, age: Int) {   //指定初始化器
        self.name = name
        self.age = age
    }
    //便捷初始化器
    convenience init() {
        self.init(name: "[unNamed]", age: 0)   //便捷初始化器要先委托同类中的其他初始化器
    }
}

class Teacher: Person {
    var salary: Int
    //前面使用了必要初始化器,子类就必须实现该初始化器
    required init(name: String, age: Int) {
        self.salary = 5000
        super.init(name: name, age: age)
    }
    //根据安全检查要求:在指定初始化器中, 在子类的初始化器中
    init(name: String, age: Int, salary: Int) {
        //必须要先将子类引入的属性初始化完成
        self.salary = salary;
        //必须先向上委托父类初始化器
        super.init(name: name, age: age)
        //初始化必须要在第一阶段初始化(就是初始化父类之后)完成之后才能调用实例方法,读取属性值
        self.test()
        //才能为继承的属性赋新值
        self.name = name + "老师"
    }
    
    //子类可以重写父类的初始化器,只要在init关键字之前加上 override修饰符即可
//    override init(name: String, age: Int) {
//        self.salary = 5000;
//        super.init(name: name, age: age)
//    }
    //便捷初始化器
    convenience init(salary: Int) {
        //便捷初始化器必须先委托同类中的其他初始化器
        self.init(name: "zhangsan", age: 30, salary: 5000)
        //然后再为任意属性赋新值
        self.salary = salary + 1000
    }
    func test() {
        print("I am a teacher")
    }
    func showInfo() {
        print("teacher name \(name), age \(age), salary \(salary)")
    }
}
//let teacher = Teacher(name: "lisi", age: 23, salary: 3000)
let teacher = Teacher(salary: 4000)

print("name \(teacher.name), age \(teacher.age), salary \(teacher.salary)")


//可失败初始化器:在init关键字后面加上问号(?),可以创建一个合适类型的可选项实例
class People {
    var name: String
    var age: Int
    //使用可失败初始化器
    init?(name: String, age: Int) {
        if age > 200 {
            return nil
        }
        self.name = name
        self.age = age
    }
}
//var p = People(name: "zhangsan", age: 300)
//print(p ?? 100)   //实际上返回的是nil

/**
 反初始化:
 1. 在类实例被释放的时候,反初始化器就会被立即调用. 你可以用deinit关键字来写反初始化器, 就如同写初始化器要用init关键字一样. 反初始化器只在类类型中有效(在枚举和结构体中无效)
 2. 反初始化器会在实例被释放之前自动被调用. 不能自行调用反初始化器. 父类的反初始化器可以被子类继承, 并且子类的反初始化器实现结束之后父类的反初始化器会被调用. 父类的反初始化器总会被调用, 就算子类没有反初始化器
 3. 每个类中只有一个反初始化器. 反初始化器不接收任何形式参数,并且不需要写圆括号
*/
//deinit {  //反初始化器是自行调用的
//    //反初始化器内的代码
//}


//类的继承


/**
 定义基类
 1. 任何不从另一个类继承的类都是基类
 2. Swift不会从一个通用基类继承.没有指定特定父类的类都会以基类的形式创建
*/
class Vehicle {
    //阻止子类重写父类中的方法和属性,可以在方法,属性或者下标脚本的关键字前写final修饰符
    var currentSpeed = 0.0
    var desc: String {
        return "currentSpeed is \(currentSpeed)"
    }
    func makeNoise() {
        
    }
}
//定义一个子类继承Vehicle
class Bicycle: Vehicle {
    var hasBasket = false
}
let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0
print("bicycle: \(bicycle.desc)")

//子类可以重写父类的实例方法,类型方法,实例属性,类型属性和下标zhujiao下标脚本. 需要在重写定义的前面加上override关键字
class Car: Vehicle {
    var gear = 0.0
    override var desc: String {   //重写父类的属性前面要加override关键字
        return super.desc + " and the gear \(gear) at now"   //在子类中,子类调用父类的属性,需要使用super关键字
    }
    //重写方法(也必须要在方法前面加上override关键字)
    override func makeNoise() {
        print("呼呼呼呼!!!!")
    }
}
let car = Car()
car.gear = 5.0
print("desc is \(car.desc)")  //输出了重写的desc属性
car.makeNoise()


/**
 重写属性的观察器:
 1. 可以使用属性的重写来为集成的属性添加属性观察器.
 2. 不能给继承而来的常量存储属性或者只读的计算属性添加属性观察器.
 3. 不能为同一个属性同时提供重写的setter和重写的属性观察器.
 */

//重写属性的观察器
class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Double(Int(Int(currentSpeed / 10.0) + 1))
        }
    }
}
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("automatic is \(automatic.desc)")




